package edu.hdu.hangzhe.utils;

import edu.hdu.hangzhe.bytecode.domain.ABI;
import edu.hdu.hangzhe.bytecode.domain.LCSResult;
import edu.hdu.hangzhe.bytecode.domain.LCSUnit;
import javafx.util.Pair;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ABILUtils {
    public static boolean isVarIns(ABI a) {
        String inst = a.inst;
        return inst.equals("ALOAD") || inst.equals("ILOAD") || inst.equals("LLOAD") || inst.equals("FLOAD") || inst.equals("DLOAD");
    }
    public static boolean isLDCIns(ABI a) {
        String inst = a.inst;
        return inst.equals("LDC");
    }
    public static boolean isGETPUTIns(ABI a) {
        String inst = a.inst;
        return inst.equals("GETSTATIC") || inst.equals("PUTSTATIC") || inst.equals("GETFIELD") || inst.equals("PUTFIELD");
    }
    public static boolean isInvokeIns(ABI a) {
        String inst = a.inst;
        return inst.equals("INVOKEVIRTUAL") || inst.equals("INVOKESPECIAL") || inst.equals("INVOKESTATIC") || inst.equals("INVOKEINTERFACE") || inst.equals("INVOKEDYNAMIC");
    }
    public static boolean isJumpIns(ABI a) {
        String inst = a.inst;
        return inst.equals("IFEQ") || inst.equals("IFNE") || inst.equals("IFLT") || inst.equals("IFGE") || inst.equals("IFGT") ||
                inst.equals("IFLE") || inst.equals("IF_ICMPEQ") || inst.equals("IF_ICMPNE") || inst.equals("IF_ICMPLT") || inst.equals("IF_ICMPGE") ||
                inst.equals("IF_ICMPGT") || inst.equals("IF_ICMPLE") || inst.equals("IF_ACMPEQ") || inst.equals("IF_ACMPNE") || inst.equals("GOTO") ||
                inst.equals("JSR") || inst.equals("IFNULL") || inst.equals("IFNONNULL");
    }
    /**
     * 获取两个ABIL的最长子序列
     * @param l
     * @param r
     * @return
     */
    public static Set<ArrayList<LCSUnit>> getLCS(ArrayList<ABI> l, ArrayList<ABI> r) {
        int[][] pd = new int[l.size() + 1][r.size() + 1];
        ABI[] larr = new ABI[l.size()];
        larr =  l.toArray(larr);
        ABI[] rarr = new ABI[r.size()];
        rarr =  r.toArray(rarr);
        pd[0][0] = 0;
        ArrayList<ABI> tmp = new ArrayList<>();
        for (int k = 1; k <= l.size(); ++k) {
            for (int kk = 1; kk <= r.size(); ++kk) {
                if (larr[k - 1].equals(rarr[kk - 1])) {
                    pd[k][kk] = pd[k - 1][kk - 1] + 1;
                    tmp.add(larr[k - 1]);
                } else {
                    pd[k][kk] = Math.max(pd[k][kk - 1], pd[k - 1][kk]);
                }
            }
        }
        Map<Integer, ArrayList<Pair<Integer, Integer>>> record = new HashMap<>();
        HashMap<Integer, Pair<Integer, Integer>> _record = new HashMap<>();
        getMapWithLCS(record, _record, l, r, tmp, 0, 0, 0);
        Set<ArrayList<LCSUnit>> res = new HashSet<>();
        ArrayList<LCSUnit> _t = new ArrayList<>();
        getAllResult(record, res, _t, 0);
        return res;
    }

    private static void getAllResult(Map<Integer, ArrayList<Pair<Integer, Integer>>> record, Set<ArrayList<LCSUnit>> res, ArrayList<LCSUnit> t, int i) {
        ArrayList<Pair<Integer, Integer>> list = record.get(i);
        if (list == null) {
            res.add(t);
            return;
        }
        for (Pair<Integer, Integer> pair : list) {
            LCSUnit unit = new LCSUnit();
            unit.m = i;
            unit.l = pair.getKey();
            unit.r = pair.getValue();
            ArrayList<LCSUnit> _t = new ArrayList<>(t);
            _t.add(unit);
            getAllResult(record, res, _t, i + 1);
        }

    }

    /**
     * @param record 记录组合
     * @param _record 记录subABIL中的编号与ABIL的映射关系
     * @param ABILi ABIL
     * @param ABILj ABIL
     * @param subABIL subABIL
     * @param ki,kj ABIL起始编号
     * @param l subABIL起始编号
     */
    private static void getMapWithLCS(Map<Integer, ArrayList<Pair<Integer, Integer>>> record,
                                      HashMap<Integer, Pair<Integer, Integer>> _record,
                                      ArrayList<ABI> ABILi, ArrayList<ABI> ABILj,
                                      ArrayList<ABI> subABIL, int ki, int kj, int l) {

        // over
        if (l == subABIL.size()) {
            for (Map.Entry<Integer, Pair<Integer, Integer>> entry : _record.entrySet()) {
                ArrayList<Pair<Integer, Integer>> a = record.computeIfAbsent(entry.getKey(), k1 -> new ArrayList<>());
                a.add(entry.getValue());
            }
            System.out.println(_record);
            return;
        }
        if (ki == ABILi.size() || kj == ABILi.size()) {
            return;
        }

        int _i = ki;
        for (; _i < ABILi.size(); ++_i) {
            int _j = kj;
            for (; _j < ABILj.size(); ++_j) {
                if (ABILi.get(_i).equals(subABIL.get(l)) && ABILj.get(_j).equals(subABIL.get(l))) {
                    _record.put(l, new Pair<>(_i, _j));
                    HashMap<Integer, Pair<Integer, Integer>> clon = new HashMap<>(_record);
                    getMapWithLCS(record, clon, ABILi, ABILj, subABIL, _i + 1, _j + 1, l + 1);
                }
            }
        }

    }

    /**
     * 判断相对于lcs，ABI是否存在
     * @param lcsMap
     * @param k 编号
     * @param isOld
     * @return
     */
    public static boolean existABI(ArrayList<LCSUnit> lcsMap, int k, boolean isOld) {
        for (LCSUnit unit : lcsMap) {
            if (isOld) {
                if (unit.l == k) return true;
            } else {
                if (unit.r == k) return true;
            }
        }
        return false;
    }

    public static int A2B4LCSMap(ArrayList<LCSUnit> lcsMap, int a, boolean isOld2New) {
        for (LCSUnit unit : lcsMap) {
            if (isOld2New) {
                if (unit.l == a) return unit.r;
            } else {
                if (unit.r == a) return unit.l;
            }
        }
        return -1;
    }

    /**
     * 简化函数参数
     * @example ([Ljava/lang/Object;)Z -> ([L)Z
     * @param args
     * @param returnType
     * @return
     */
    public static String simplifyType(String args, String returnType) {
        args = "(" + args + ")" + returnType;
        StringBuilder res = new StringBuilder();
        int offset = 0;
        boolean Lflag = false;
        while (offset < args.length()) {
            if (!Lflag && args.charAt(offset) == 'L') {
                res.append("L");
                Lflag =true;
                ++offset;
                continue;
            }
            if (args.charAt(offset) == ';') {
                Lflag =false;
                ++offset;
                continue;
            }
            if (Lflag) {
                ++offset;
                continue;
            }
            res.append(args.charAt(offset));
            ++offset;
        }
        return res.toString();
    }
}
